package pl.edu.mimuw.mas.agent.behaviour.helicopter.verification;

import jade.content.lang.Codec.CodecException;
import jade.content.onto.OntologyException;
import jade.content.onto.UngroundedException;
import jade.core.AID;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;
import jade.lang.acl.UnreadableException;

import java.io.IOException;
import java.util.HashSet;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import pl.edu.mimuw.mas.agent.HelicopterAgent;
import pl.edu.mimuw.mas.agent.behaviour.ServerBehaviour;
import pl.edu.mimuw.mas.message.PositionMessage;
import pl.edu.mimuw.mas.message.VerifyOfferMessage;

/**
 * Zachowanie wybierające najlepszą ofertę z przetargu na weryfikację łosia.
 * Jeśli przetarg zakończy się sukcesem to dodawane jest zachowanie
 * {@link ListenVerifyBehaviour} w przeciwnym przypadku wykonywana jest metoda
 * doPatrol agenta.
 */
public class BestAgentPickerBehaviour extends ServerBehaviour {
	private static final long serialVersionUID = 2547813982195201201L;

	private final Logger logger = LoggerFactory.getLogger(getClass());
	
	private HashSet<AID> agents;
	private AID bestAgent;
	private float bestCost;
	private int total;
	
	public BestAgentPickerBehaviour(HelicopterAgent agent) {
		super(agent);
		this.agents = new HashSet<AID>(agent.getHelicopterCount());
		this.bestCost = Float.MAX_VALUE;
		this.total = agent.getHelicopterCount();
		
		MessageTemplate mt = MessageTemplate.and(
				MessageTemplate.MatchConversationId(HelicopterAgent.HELICOPTER_VERIFY_CONV),
				MessageTemplate.or(
					MessageTemplate.MatchPerformative(ACLMessage.PROPOSE), 
					MessageTemplate.MatchPerformative(ACLMessage.REFUSE)
				)
			);
		setMessageTemplate(mt);
	}

	public boolean doAction(ACLMessage msg, ACLMessage reply) throws UngroundedException, CodecException, OntologyException, UnreadableException, IOException {
		HelicopterAgent ha = (HelicopterAgent) myAgent;

		if(msg.getPerformative() == ACLMessage.PROPOSE) {
			VerifyOfferMessage offer = (VerifyOfferMessage) msg.getContentObject();
			agents.add(offer.getAID());		
			
			if(offer.getCost() < bestCost) {
				bestAgent = offer.getAID();
				bestCost = offer.getCost();
			}
			
			logger.info("{}: Dostałem propozycję " + offer.getCost() + " od {}", myAgent.getName(), msg.getSender().getName());
			ha.log("Dostałem propozycję " + offer.getCost() + " od " + msg.getSender().getName());
		} else {
			total--;
			logger.info("{}: Dostałem odmowę od {}", myAgent.getName(), msg.getSender().getName());
			ha.log("Dostałem odmowę od " + msg.getSender().getName());
		}
		
		// Zebrałem wszystkich agentów - trzeba wybrać
		if(agents.size() == total) {
			logger.info("{}: Dostałem wszystkie oferty", myAgent.getName());
			ha.log("Dostałem wszystkie oferty");
			
			// wyslij potwierdzenie do najlepszego agenta
			if(bestAgent != null) {
				ACLMessage acceptReply = msg.createReply();
				acceptReply.clearAllReceiver();
				
				acceptReply.setPerformative(ACLMessage.ACCEPT_PROPOSAL);
				acceptReply.addReceiver(bestAgent);
				acceptReply.setContentObject(new PositionMessage(ha.getAID(), ha.getPosition()));
				ha.send(acceptReply);
				
				ACLMessage rejectReply = msg.createReply();
				rejectReply.clearAllReceiver();
				
				rejectReply.setPerformative(ACLMessage.REJECT_PROPOSAL);
				for (AID aid : agents) {
					if (!aid.equals(bestAgent)) {
						rejectReply.addReceiver(aid);
					} else if(logger.isTraceEnabled()) {
						logger.trace("{}: Omijam aid {}", myAgent.getName(), aid.getName());
					}
				}				
				ha.send(rejectReply);
				
				ha.addBehaviour(new ListenVerifyBehaviour(ha, bestAgent));				
			} else {
				// Jak sie nie uda
				logger.info("{}: Nie udało się rozstrzygnąć przetargu", myAgent.getName());
				ha.log("Nie udało się rozstrzygnąć przetargu");
				ha.doPatrol();
			}
			
			stop();
		}
		
		return false;
	}
}
